package com.apes.framework.plugin.event;

import com.apes.framework.plugin.esb.api.Request;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.oap.RopContext;
import com.apes.oap.event.AfterStartedRopEvent;
import com.apes.oap.event.RopEventListener;
import com.apes.oap.impl.SimpleRopRequestContext;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * 功能：topic消息监听器注册
 *
 * @author xul
 * @create 2019-01-15 13:45
 */
@Component
public class RegistryEventListener implements RopEventListener<AfterStartedRopEvent> {
    private Map<String, ApesEventListener> listeners = new HashMap<>();

    /**
     * 多事件分隔符
     **/
    private final static String EVENT_SEPARATOR = ",";

    @Override
    public void onRopEvent(AfterStartedRopEvent oapEvent) {
        RopContext ropContext = oapEvent.getRopContext();
        ApplicationContext context = ropContext.getApplicationContext();
        ApesEventMulticaster multicaster = context.getBean(ApesEventMulticaster.class);
        multicaster.setExecutor((Executor) context.getBean("asyncEventExecutor"));
        for (String beanName : context.getBeanNamesForType(Object.class)) {
            ReflectionUtils.doWithMethods(Objects.requireNonNull(context.getType(beanName)),
                    (method) -> {
                        ReflectionUtils.makeAccessible(method);
                        Listener annotation = AnnotationUtils.findAnnotation(method, Listener.class);
                        assert annotation != null;
                        String[] topics = annotation.topic().trim().split(EVENT_SEPARATOR);
                        Arrays.stream(topics).forEach((String tpc) -> {
                            ApesEventListener listener = new ApesEventListener() {
                                /**
                                 * 响应事件
                                 *
                                 * @param event
                                 */
                                @Override
                                public void onEvent(ApesEvent event) {
                                    SimpleRequest request = new SimpleRequest();
                                    request.setRopRequestContext(new SimpleRopRequestContext(ropContext));
                                    request.setData(event.getBody());
                                    Object target = context.getBean(beanName);
                                    Object result = ReflectionUtils.invokeMethod(method, target, request);
                                    event.setResult(result);
                                }

                                /**
                                 * 执行的顺序号
                                 *
                                 * @return
                                 */
                                @Override
                                public int getOrder() {
                                    return annotation.order();
                                }

                                /**
                                 * 监听主题
                                 *
                                 * @return
                                 */
                                @Override
                                public String getTopic() {
                                    return tpc;
                                }
                            };
                            multicaster.addListener(listener);
                            listeners.put(beanWithMethod(beanName, method.getName()), listener);
                        });
                    },
                    (method) -> !method.isSynthetic() && AnnotationUtils.findAnnotation(method, Listener.class) != null
                            && method.getParameterTypes().length == 1
                            && ClassUtils.isAssignable(Request.class, method.getParameterTypes()[0])
                            && !listeners.containsKey(beanWithMethod(beanName, method.getName())));
        }
    }

    private String beanWithMethod(String bean, String method) {
        return bean + "#" + method;
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
